﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace DotNetCommon.Extensions
{
    /// <summary>
    /// <see cref="IList{T}"/> 的扩展方法
    /// </summary>
    public static class ListExtensions
    {
        /// <summary>
        /// 主要用来做List的初始化: <code>new List&lt;int>{1,new int[]{2,3},4}</code>
        /// </summary>
        /// <returns>返回自身</returns>
        public static IList<T> Add<T>(this IList<T> list, IEnumerable<T> items)
        {
            if (list == null) return list;
            foreach (var item in items)
            {
                list.Add(item);
            }
            return list;
        }

        /// <summary>
        /// 将给定的<paramref name="items"/>添加到给定的<paramref name="list"/>。
        /// </summary>
        /// <returns>返回自身</returns>
        public static IList<T> AddRange<T>(this IList<T> list, IEnumerable<T> items)
        {
            if (list == null) return list;
            foreach (var item in items)
            {
                list.Add(item);
            }
            return list;
        }

        /// <summary>
        /// 从指定的集合中移除符合条件的元素
        /// </summary>
        /// <returns>返回自身</returns>
        public static IList<T> Remove<T>(this IList<T> list, Func<T, bool> predicate)
        {
            if (list == null) return list;
            list.Where(predicate).ToList().ForEach(t =>
            {
                if (list.Contains(t)) list.Remove(t);
            });
            return list;
        }

        #region Fluent风格 List<T> IList<T> AddFluent AddIfNotExistFluent AddRangeFluent AddRangeIfNotExistFluent ClearFluent RemoveAtFluent InsertAtFluent
        /// <summary>
        /// Fluent风格: 向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> AddFluent<T>(this List<T> list, T item)
        {
            list.Add(item);
            return list;
        }

        /// <summary>
        /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> AddIfNotExistFluent<T>(this List<T> list, T item)
        {
            return AddIfNotExistFluent(list, item, EqualityComparer<T>.Default);
        }

        /// <summary>
        /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> AddIfNotExistFluent<T>(this List<T> list, T item, IEqualityComparer<T> compare)
        {
            compare ??= EqualityComparer<T>.Default;
            var idx = list.FindIndex(t => compare.Equals(t, item));
            if (idx < 0) list.Add(item);
            return list;
        }

        /// <summary>
        /// Fluent风格: 向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> AddFluent<T>(this IList<T> list, T item)
        {
            list.Add(item);
            return list;
        }

        /// <summary>
        /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> AddIfNotExistFluent<T>(this IList<T> list, T item)
        {
            return AddIfNotExistFluent(list, item, EqualityComparer<T>.Default);
        }

        /// <summary>
        /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> AddIfNotExistFluent<T>(this IList<T> list, T item, IEqualityComparer<T> compare)
        {
            compare ??= EqualityComparer<T>.Default;
            for (var idx = 0; idx < list.Count; idx++)
            {
                if (compare.Equals(list[idx], item)) return list;
            }
            list.Add(item);
            return list;
        }

        /// <summary>
        /// Fluent风格: 向集合中添加新项集合,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> AddRangeFluent<T>(this List<T> list, IEnumerable<T> items)
        {
            if (items.IsNullOrEmpty()) return list;
            foreach (var item in items) list.Add(item);
            return list;
        }

        /// <summary>
        /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> AddRangeIfNotExistFluent<T>(this List<T> list, IEnumerable<T> items)
        {
            return AddRangeIfNotExistFluent(list, items, EqualityComparer<T>.Default, false);
        }

        /// <summary>
        /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> AddRangeIfNotExistFluent<T>(this List<T> list, IEnumerable<T> items, bool isDistinct)
            => AddRangeIfNotExistFluent(list, items, EqualityComparer<T>.Default, isDistinct);

        /// <summary>
        /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> AddRangeIfNotExistFluent<T>(this List<T> list, IEnumerable<T> items, IEqualityComparer<T> compare)
            => AddRangeIfNotExistFluent(list, items, compare, false);

        /// <summary>
        /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> AddRangeIfNotExistFluent<T>(this List<T> list, IEnumerable<T> items, IEqualityComparer<T> compare, bool isDistinct)
        {
            compare ??= EqualityComparer<T>.Default;
            if (isDistinct)
            {
                list.AddRange(items);
                for (var i = 0; i < list.Count - 1; i++)
                {
                    for (var j = i + 1; j < list.Count; j++)
                    {
                        if (compare.Equals(list[j], list[i]))
                        {
                            list.RemoveAt(j);
                            j--;
                        }
                    }
                }
                return list;
            }
            foreach (var item in items)
            {
                var idx = list.FindIndex(t => compare.Equals(t, item));
                if (idx < 0) list.Add(item);
            }
            return list;
        }

        /// <summary>
        /// Fluent风格: 向集合中添加新项集合,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> AddRangeFluent<T>(this IList<T> list, IEnumerable<T> items)
        {
            if (items.IsNullOrEmpty()) return list;
            foreach (var item in items) list.Add(item);
            return list;
        }

        /// <summary>
        /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> AddRangeIfNotExistFluent<T>(this IList<T> list, IEnumerable<T> items)
        {
            return AddRangeIfNotExistFluent(list, items, EqualityComparer<T>.Default, false);
        }

        /// <summary>
        /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> AddRangeIfNotExistFluent<T>(this IList<T> list, IEnumerable<T> items, bool isDistinct)
            => AddRangeIfNotExistFluent(list, items, EqualityComparer<T>.Default, isDistinct);

        /// <summary>
        /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> AddRangeIfNotExistFluent<T>(this IList<T> list, IEnumerable<T> items, IEqualityComparer<T> compare)
            => AddRangeIfNotExistFluent(list, items, compare, false);

        /// <summary>
        /// Fluent风格: 如果集合中不存在,则向集合中添加新项,并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> AddRangeIfNotExistFluent<T>(this IList<T> list, IEnumerable<T> items, IEqualityComparer<T> compare, bool isDistinct)
        {
            compare ??= EqualityComparer<T>.Default;
            if (isDistinct)
            {
                list.AddRange(items);
                for (var i = 0; i < list.Count - 1; i++)
                {
                    for (var j = i + 1; j < list.Count; j++)
                    {
                        if (compare.Equals(list[j], list[i]))
                        {
                            list.RemoveAt(j);
                            j--;
                        }
                    }
                }
                return list;
            }
            foreach (var item in items)
            {
                var hasFound = false;
                for (var i = 0; i < list.Count; i++)
                {
                    if (compare.Equals(list[i], item))
                    {
                        hasFound = true;
                        break;
                    }
                }
                if (!hasFound) list.Add(item);
            }
            return list;
        }

        /// <summary>
        /// Fluent风格: 清空集合并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> ClearFluent<T>(this List<T> list)
        {
            list.Clear();
            return list;
        }

        /// <summary>
        /// Fluent风格: 清空集合并返回自身
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> ClearFluent<T>(this IList<T> list)
        {
            list.Clear();
            return list;
        }

        /// <summary>
        /// Fluent风格: 从集合中移除某项
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> RemoveAtFluent<T>(this List<T> list, int index)
        {
            list.RemoveAt(index);
            return list;
        }

        /// <summary>
        /// Fluent风格: 从集合中移除某项
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> RemoveAtFluent<T>(this IList<T> list, int index)
        {
            list.RemoveAt(index);
            return list;
        }

        /// <summary>
        /// Fluent风格: 向集合中插入某项
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> InsertAtFluent<T>(this List<T> list, int index, T b)
        {
            list.Insert(index, b);
            return list;
        }

        /// <summary>
        /// Fluent风格: 向集合中插入某项
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> InsertAtFluent<T>(this IList<T> list, int index, T b)
        {
            list.Insert(index, b);
            return list;
        }

        /// <summary>
        /// Fluent风格: 设置集合中指定索引的值
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null或索引超出,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> SetFluent<T>(this IList<T> list, int index, T b)
        {
            list[index] = b;
            return list;
        }

        /// <summary>
        /// Fluent风格: 设置集合中指定索引的值
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null或索引超出,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> SetFluent<T>(this IList<T> list, int index, Func<T, T> func)
        {
            if (func == null) return list;
            list[index] = func(list[index]);
            return list;
        }

        /// <summary>
        /// Fluent风格: 批量设置集合中的值
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null或索引超出,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> SetFluent<T>(this IList<T> list, Func<T, bool> filter, Func<T, T> func)
        {
            if (func == null) return list;
            for (int i = 0; i < list.Count; i++)
            {
                var b = filter?.Invoke(list[i]);
                if (b == false) continue;
                list[i] = func(list[i]);
            }
            return list;
        }

        /// <summary>
        /// Fluent风格: 设置集合中指定索引的值
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null或索引超出,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> SetFluent<T>(this List<T> list, int index, T b)
        {
            list[index] = b;
            return list;
        }

        /// <summary>
        /// Fluent风格: 设置集合中指定索引的值
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null或索引超出,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> SetFluent<T>(this List<T> list, int index, Func<T, T> func)
        {
            if (func == null) return list;
            list[index] = func(list[index]);
            return list;
        }

        /// <summary>
        /// Fluent风格: 批量设置集合中的值
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null或索引超出,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> SetFluent<T>(this List<T> list, Func<T, bool> filter, Func<T, T> func)
        {
            if (func == null) return list;
            for (int i = 0; i < list.Count; i++)
            {
                var b = filter?.Invoke(list[i]);
                if (b == false) continue;
                list[i] = func(list[i]);
            }
            return list;
        }

        /// <summary>
        /// Fluent风格: 从集合中移除某项
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static IList<T> RemoveFluent<T>(this IList<T> list, T b)
        {
            var idx = list.IndexOf(b);
            if (idx >= 0) list.RemoveAt(idx);
            return list;
        }

        /// <summary>
        /// Fluent风格: 从集合中移除某项
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> RemoveFluent<T>(this List<T> list, T b)
        {
            var idx = list.IndexOf(b);
            if (idx >= 0) list.RemoveAt(idx);
            return list;
        }

        /// <summary>
        /// Fluent风格: 从集合中移除某项
        /// </summary>
        /// <remarks>
        /// 注意:
        /// <list type="bullet">
        /// <item>如果集合为null,抛出异常</item>
        /// </list>
        /// </remarks>
        public static List<T> RemoveFluent<T>(this List<T> list, Predicate<T> match)
        {
            var idx = list.FindIndex(0, match);
            if (idx >= 0) list.RemoveAt(idx);
            return list;
        }
        #endregion

        #region 循环操作 Next/Pre/ReverseFluent/ReSetStart
        /// <summary>
        /// 循环操作场景(一个多边形的点位遍历), 返回指定索引的下一个元素,如:
        /// <code>
        /// new List&lt;Vector2> { new(0, 0), new(1, 0), new(1, 1) }.Next(2).ShouldBe(new(0, 0));
        /// </code>
        /// </summary>
        public static T Next<T>(this IList<T> list, int index)
        {
            return list[(index + 1) % list.Count];
        }
        /// <summary>
        /// 循环操作场景(一个多边形的点位遍历),返回指定索引的上一个元素,如:
        /// <code>
        /// new List&lt;Vector2> { new(0, 0), new(1, 0), new(1, 1) }.Pre(0).ShouldBe(new(1, 1));
        /// </code>
        /// </summary>
        public static T Pre<T>(this IList<T> list, int index)
        {
            return list[(index - 1 + list.Count) % list.Count];
        }
        /// <summary>
        /// 将自身翻转, 并返回自身,如下:
        /// <code>
        /// new List&lt;int> { 1, 2, 3, 4 }.ReverseFluent().ShouldBe(new List&lt;int> { 4, 3, 2, 1 })
        /// </code>
        /// </summary>
        public static List<T> ReverseFluent<T>(this List<T> list)
        {
            list.Reverse();
            return list;
        }
        /// <summary>
        /// 将自身翻转, 并返回自身,如下:
        /// <code>
        /// new List&lt;int> { 1, 2, 3, 4 }.ReverseFluent().ShouldBe(new List&lt;int> { 4, 3, 2, 1 })
        /// </code>
        /// </summary>
        public static IList<T> ReverseFluent<T>(this IList<T> list)
        {
            list.Reverse();
            return list;
        }
        /// <summary>
        /// 旋转集合, 使 <c>index</c> 位置的元素作为新的起始元素, 返回新的集合, 如下:
        /// <code>
        /// new List&lt;int> { 1, 2, 3, 4 }.ReSetStart(2).ShouldBe(new List&lt;int> { 3, 4, 1, 2 });
        /// new List&lt;int> { 1, 2, 3, 4 }.ReSetStart(-1).ShouldBe(new List&lt;int> { 4, 1, 2, 3 });
        /// </code>
        /// </summary>
        public static List<T> ReSetStart<T>(this List<T> list, int index, bool reverse = false)
        {
            index %= list.Count;
            if (index < 0) index += list.Count;
            if (reverse)
            {
                return list.Take(index + 1).Reverse().Concat(list.Skip(index + 1).Take(list.Count - index - 1).Reverse()).ToList();
            }
            else
            {
                return list.Skip(index).Take(list.Count - index).Concat(list.Take(index)).ToList();
            }
        }
        /// <summary>
        /// 旋转集合, 使 <c>index</c> 位置的元素作为新的起始元素, 返回新的集合, 如下:
        /// <code>
        /// new List&lt;int> { 1, 2, 3, 4 }.ReSetStart(2).ShouldBe(new List&lt;int> { 3, 4, 1, 2 });
        /// new List&lt;int> { 1, 2, 3, 4 }.ReSetStart(-1).ShouldBe(new List&lt;int> { 4, 1, 2, 3 });
        /// </code>
        /// </summary>
        public static IList<T> ReSetStart<T>(this IList<T> list, int index, bool reverse = false)
            => ReSetStart(list.ToList(), index, reverse);
        #endregion

        #region IList.FindIndex/FindLastIndex
        /// <summary>
        /// 类似 list.FindIndex
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="match"></param>
        /// <returns></returns>
        public static int FindIndex<T>(this IList<T> list, Predicate<T> match)
        {
            for (int i = 0; i < list.Count; i++)
            {
                if (match(list[i])) return i;
            }
            return -1;
        }
        /// <summary>
        /// 类似 list.FindLastIndex
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="list"></param>
        /// <param name="match"></param>
        /// <returns></returns>
        public static int FindLastIndex<T>(this IList<T> list, Predicate<T> match)
        {
            for (int i = list.Count - 1; i >= 0; i--)
            {
                if (match(list[i])) return i;
            }
            return -1;
        }
        #endregion
    }
}